classdef ScopeModule < EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule
    %SCOPEMODULE Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        device
        inputNode
        samplingRate
        domain
    end
    
    methods
        function obj = ScopeModule(server, name)
            %SCOPEMODULE Construct an instance of this class
            %   Detailed explanation goes here
            obj@EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule(server, name, 'scopeModule');
        end
        
        function initialize(obj)
            initialize@EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule(obj);
            obj.setDevice(obj.server.deviceSerials{1});
            obj.setModuleSettingValue(ziSettingsScopeModule.mode, 1);
            obj.setModuleSettingValue(ziSettingsScopeModule.averager_weight, 1);
            obj.setModuleSettingValue(ziSettingsScopeModule.historylength, 1);
            obj.sync();
            
            obj.measured_data = {};
            obj.disableTrigger().disableSegment().disableScope();
        end
    end
    
    %%
    methods
        function obj = setDevice(obj, dev)
            L = log4m.getLogger;
            getSession;
            obj.device = sess.getDevice(dev);            
            L.info('ScopeModule::setDevice', sprintf('Device set to [%s].', dev));
        end
        
        function obj = enableScope(obj)
            obj.device.scopeEnable();
        end
        
        function obj = disableScope(obj)
            obj.device.scopeDisable();
        end
        
        function obj = setSingleShot(obj)
            obj.device.scopeSetSingleShot();
        end
        function obj = setContinuous(obj)
            obj.device.scopeSetContinuous();
        end
        
        function obj = setSamplingRate(obj, index)
            % set sampling rate: for MFLI rate = 60MHz/ (2^index)
            p=inputParser;
            p.addRequired('index', @ziEnumScopeSamplingRate.test);
            p.parse(index);
            
            zi = obj.device;
            zi.scopeSetSamplingRate(index); pause(0.1);
            obj.samplingRate = 60e6./double(2.^index);            
        end
        
        function obj = setDuration(obj, time)
            L = log4m.getLogger;
            p=inputParser;
            p.addRequired('time', @(x) x>0.0);
            p.parse(time);
            
            zi = obj.device; 
            rate = zi.scopeGetSamplingRate();
            
            maxScopeLength = 4096;
            actual_length = max(2.^ceil(log2(rate*time)), maxScopeLength);
            zi.scopeSetLength( actual_length ); pause(0.2);
            
            actual_time = actual_length/rate;
            L.info('ScopeModule::setDuration', sprintf('Actual Duration is [%3.2e] s', actual_time));
        end
        
        function obj = setInput(obj, channel1, varargin)
            p=inputParser;
            p.addRequired('channel1', @ziScopeInput.test);
            p.addOptional('channel2', [], @(x) ziScopeInput.test(x) || isempty(x) );
            p.parse(channel1, varargin{:});
            
            zi = obj.device;
            zi.scopeInputSelect('0', p.Results.channel1);
            obj.inputNode = {p.Results.channel1};
            if ~isempty(p.Results.channel2)
                zi.scopeInputSelect('1', p.Results.channel2); pause(0.1);
                obj.inputNode = {p.Results.channel1, p.Results.channel2};
            end
        end
        
        function obj = enableChannel(obj, channel)
            % enable channel
            % input: 1 - channel 1 enabled, 2 - channel 2 enabled, 3 - both channels enabled
            p = inputParser;            
            p.addRequired('channel',  @ziEnumTwoBitChoice.test);
            p.parse(channel);
            obj.device.scopeEnableChannel(p.Results.channel);
        end
        
        function obj = setTrigger(obj, trigNode, varargin)
            p=inputParser();
            p.addRequired('trigNode', @ziScopeTrigInput.test);
            p.addParameter('slope',          ziEnumTrigEdgeSlope.Positive,      @ziEnumTrigEdgeSlope.test );
            p.addParameter('level',          0.0,                               @isnumeric);
            p.addParameter('hysteresisMode', 0,                                 @(x) ismember(x, [0 1]));
            p.addParameter('hysteresis',     0.0,                               @isnumeric);
            p.addParameter('enableGating',   false,                             @islogical);
            p.addParameter('gating',         ziEnumScopeTrigGating.TrigIn1High, @ziEnumScopeTrigGating.test);
            p.addParameter('holdoffmode',    0,                                 @(x) ismember(x, [0 1]));
            p.addParameter('holdofftime',    0.0,                               @(x) x>=0.0 );
            p.addParameter('holdoffcount',   0,                                 @(x) x>=0 );
            p.addParameter('reference',      0.5,                               @(x) x>=0 && x<=1.0 );
            p.addParameter('delay',          0.0,                               @isnumeric );
            p.parse(trigNode, varargin{:});
            
            zi=obj.device;
            zi.scopeTrigNodeSelect(p.Results.trigNode);
            zi.scopeSetTrigEdgeSlope(p.Results.slope);
            zi.scopeSetTrigLevel(p.Results.level);
            zi.scopeSetTrigHoldoffMode(p.Results.hysteresisMode);
            switch p.Results.hysteresisMode
                case 0
                    zi.scopeSetTrigHysteresisAbsolute(p.Results.hysteresis);
                case 1
                    zi.scopeSetTrigHysteresisRelative(p.Results.hysteresis);
            end
            if p.Results.enableGating
                zi.scopeEnableTrigGating();
            else
                zi.scopeDisableTrigGating();
            end
            zi.scopeSetTrigGating(p.Results.gating);
            zi.scopeSetTrigHoldoffMode(p.Results.holdoffmode);
            zi.scopeSetTrigHoldoffTime(p.Results.holdofftime);
            zi.scopeSetTrigHoldoffCount(p.Results.holdoffcount);
            zi.scopeSetTrigReference(p.Results.reference);
            zi.scopeSetTrigDelay(p.Results.delay);
        end        
        function obj = enableTrigger(obj)
            obj.device.scopeTriggerEnable();
        end
        function obj = disableTrigger(obj)
            obj.device.scopeTriggerDisable();
        end
        
        function obj = setSegmet(obj, num)
            p=inputParser();
            p.addRequired('num', @(x) x>0);
            p.parse(num);
            obj.device.scopeSegmentCount(ceil(num));
        end
        function obj = enableSegment(obj)
            obj.device.scopeEnableSegment();
        end
        function obj = disableSegment(obj)
            obj.device.scopeDisableSegment();
        end
        
        function obj = enableCh1BWLimit(obj)
            obj.device.scopeEnableBandwidthLimitChannel1();
        end
        function obj = disableCh1BWLimit(obj)
            obj.device.scopeDisableBandwidthLimitChannel1();
        end
        function obj = enableCh2BWLimit(obj)
            obj.device.scopeEnableBandwidthLimitChannel2();
        end
        function obj = disableCh2BWLimit(obj)
            obj.device.scopeDisableBandwidthLimitChannel2();
        end
        function records = getRecordNumber(obj)
            records = obj.getModuleSettingValue(ziSettingsScopeModule.records);
        end
    end
    
    %%
    methods
        function res = run(obj, varargin)
            clear textprogressbar;
            L = log4m.getLogger;
            
            p=inputParser;
            p.addParameter('shot_number', 1, @(x) x>0);
            p.addParameter('plot_window', 1, @(x) ismember(x, [1 2]));
            p.addParameter('tUpdate', 1.0, @(x) x>0);
            p.addParameter('timeout', inf, @(x) x>0);
            p.parse(varargin{:});
            
            if length(obj.inputNode) == 1
                plot_win = 1;
            else
                plot_win = p.Results.plot_window;
            end
            
            obj.subscribe();
            obj.enableScope();
            if p.Results.shot_number == 1
                obj.setSingleShot();
            else
                obj.setContinuous();
            end
            obj.execute();
            obj.sync();
            
            time_start = tic; records = 0; nShot = double(p.Results.shot_number);
            textprogressbar( sprintf('%s Acquiring Data,\t', datestr(now,'yyyy-mm-dd HH:MM:SS,FFF')) ); 
            while records < nShot
                pause(p.Results.tUpdate); 
                records = double(obj.getRecordNumber()); 
                textprogressbar(100*min(records/nShot, 1.0), sprintf('Record No. = %d (%d required). ', records, p.Results.shot_number));

                obj.assembleData(obj.read(), true, plot_win);

                if toc(time_start) > p.Results.timeout
                    break
                end
            end
            if records >= nShot
                textprogressbar(100, sprintf('Record No. = %d (%d required).', records, p.Results.shot_number));
                textprogressbar('Done.');
            else
                fprintf('\n'); L.info('ScopeModule::run', sprintf('Timeout, runtime = [%3.2e]s', toc(time_start)));
            end
            
            %finialize
            obj.assembleData(obj.read(), false);
            res = EquipmentControl.ZI.ziServer.ziServerData.ziServerScopeModuleData(obj.measured_data, obj.inputNode, obj.device.device, obj.domain);
            
            obj.finish();
            obj.unsubscribe();
            obj.disableScope();
        end
        
        function obj = subscribe(obj)
            scope_path = ['/' obj.device.device '/scopes/0/wave'];
            obj.subscribed_path = {scope_path};
            subscribe@EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule(obj, scope_path);
        end
        function obj = setMode(obj, mode)
            % set mode
            % input: mode - 'frequency' or 'time'
            p=inputParser;
            p.addRequired('mode', @(x) ismember(x, {'frequency', 'time'}));
            p.parse(mode);
            
            obj.domain = p.Results.mode;
            switch p.Results.mode
                case 'time'
                    obj.setModuleSettingValue(ziSettingsScopeModule.mode, 1);
                case 'frequency'
                    obj.setModuleSettingValue(ziSettingsScopeModule.mode, 3);
            end
        end
        function obj = setMovingAverage(obj, nAverage)
            p=inputParser;
            p.addRequired('nAverage', @(x) x>0);
            p.parse(nAverage);
            obj.setModuleSettingValue(ziSettingsScopeModule.averager_weight, nAverage);
        end
        
        function obj = setFFTWindow(obj, win)
            p=inputParser;
            p.addRequired('win', @ziEnumFFTWindow.test);
            p.parse(win);
            obj.setModuleSettingValue(ziSettingsScopeModule.fft_window, win);            
        end
        
        function obj = enableSpectralDensity(obj)
            obj.setModuleSettingValue(ziSettingsScopeModule.fft_spectraldensity, 1);
        end
        function obj = disableSpectralDensity(obj)
            obj.setModuleSettingValue(ziSettingsScopeModule.fft_spectraldensity, 0);
        end
        function obj = enableFFTPower(obj)
            obj.setModuleSettingValue(ziSettingsScopeModule.fft_power, 1);
        end
        function obj = disableFFTPower(obj)
            obj.setModuleSettingValue(ziSettingsScopeModule.fft_power, 0);
        end
    end
    
    %%
    methods
        function assembleData(obj, tmp, isPlot, plot_win)
            if nargin == 3
                plot_win = 1;
            end
            if obj.isDataAvailable(tmp)
                obj.measured_data = {tmp};
                if isPlot
                    tmpData = EquipmentControl.ZI.ziServer.ziServerData.ziServerScopeModuleData(obj.measured_data, obj.inputNode, obj.device.device, obj.domain);
                    tmpData.plot_data(obj.monitor_hdl, plot_win);
                end
            end
        end
        
        function tf = isDataAvailable(obj, tmp)
            try
                tf = (tmp.records > 0) && ~isempty(tmp.(obj.device.device));
            catch
                tf = false;
            end
        end
    end
end

